/* * JCarder -- cards Java programs to keep threads disentangled * * Copyright (C) 2006-2007 Enea AB * Copyright (C) 2007 Ulrik Svensson * Copyright (C) 2007 Joel Rosdahl * * This program is made available under the GNU GPL version 2, with a special * exception for linking with JUnit. See the accompanying file LICENSE.txt for * details. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. */ package com.enea.jcarder.agent; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; import java.lang.instrument.Instrumentation; import java.util.ArrayList; import java.util.Collection; import java.util.Enumeration; import com.enea.jcarder.agent.instrument.ClassTransformer; import com.enea.jcarder.agent.instrument.InstrumentConfig; import com.enea.jcarder.util.BuildInformation; import com.enea.jcarder.util.logging.AppendableHandler; import com.enea.jcarder.util.logging.Handler; import com.enea.jcarder.util.logging.Logger; /** * This is the main class of the JCarder Java agent. It will initialize JCarder * and register a ClassTransformer that is called by the JVM each time a class * is loaded. */ public final class JavaAgent { private static final String DUMP_PROPERTY = "jcarder.dump"; private static final String LOGLEVEL_PROPERTY = "jcarder.loglevel"; private static final String LOG_FILENAME = "jcarder.log"; private final InstrumentConfig mConfig = new InstrumentConfig(); private Logger mLogger; PrintWriter mLogWriter; private File mOutputDir; private Logger.Level mLogLevel; private static final String OUTPUTDIR_PROPERTY = "jcarder.outputdir"; private JavaAgent() { } /** * This method is called by the JVM when the JVM is started with the * -javaagent command line parameter. */ public static void premain(final String args, final Instrumentation instrumentation) throws Exception { JavaAgent javaAgent = new JavaAgent(); javaAgent.init(instrumentation); } private void init(Instrumentation instrumentation) throws Exception { handleProperties(); initLogger(); mLogger.info("Starting " + BuildInformation.getShortInfo() + " agent"); logJvmInfo(); EventListener listener = EventListener.create(mLogger, mOutputDir); ClassTransformer classTransformer = new ClassTransformer(mLogger, mOutputDir, mConfig); instrumentation.addTransformer(classTransformer); StaticEventListener.setListener(listener); mLogger.info("JCarder agent initialized\n"); } private void initLogger() { File logFile = new File(mOutputDir, LOG_FILENAME); if (logFile.exists()) { logFile.delete(); } FileWriter fileWriter; try { fileWriter = new FileWriter(logFile); } catch (IOException e) { System.err.println("Failed to open log file \"" + logFile + "\": " + e.getMessage()); return; } mLogWriter = new PrintWriter(new BufferedWriter(fileWriter)); AppendableHandler fileHandler = new AppendableHandler(mLogWriter); AppendableHandler consoleHandler = new AppendableHandler(System.err, Logger.Level.INFO, "{message}\n"); Thread hook = new Thread() { public void run() { mLogWriter.flush(); } }; Runtime.getRuntime().addShutdownHook(hook); Collection<Handler> handlers = new ArrayList<Handler>(); handlers.add(fileHandler); handlers.add(consoleHandler); mLogger = new Logger(handlers, mLogLevel); } private void logJvmInfo() { Enumeration<?> properties = System.getProperties().propertyNames(); while (properties.hasMoreElements()) { String key = (String) properties.nextElement(); if (key.startsWith("java.vm.")) { mLogger.config(key + ": " + System.getProperty(key)); } } } private void handleProperties() throws IOException { handleDumpProperty(); handleLogLevelProperty(); handleOutputDirProperty(); } private void handleDumpProperty() { mConfig.setDumpClassFiles(Boolean.getBoolean(DUMP_PROPERTY)); } private void handleLogLevelProperty() { String logLevelValue = System.getProperty(LOGLEVEL_PROPERTY, "fine"); Logger.Level logLevel = Logger.Level.fromString(logLevelValue); if (logLevel != null) { mLogLevel = logLevel; } else { System.err.print("Bad loglevel; should be one of "); System.err.println(Logger.Level.getEnumeration()); System.err.println(); System.exit(1); } } private void handleOutputDirProperty() throws IOException { final String property = System.getProperty(OUTPUTDIR_PROPERTY, "."); mOutputDir = new File(property).getCanonicalFile(); if (!mOutputDir.isDirectory()) { mOutputDir.mkdirs(); } } }